/*
 * Copyright 2010 (c) Dou Yongwang (douyongwang@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "sjson_template.h"
#include "sjson_template.pan"
#if 0
//#include <FileLogger.h>
#else
#define LOGCLOSE
#define LOGARG(txt,parm...)
#define LOGTXT(txt)
#define LOGDES8(des)
#define LOGDES16(des)
#define LOGERR(txt,err)
#define LOGCALL(exp)
#define LOGENTER
#define LOGEXIT
#define LOGMEM(ptr)
#endif
_LIT8(kDefaultMeta, "{{}}");
_LIT8(kDefaultClauseName, "default");
_LIT8(kUndefinedValue, "no value found.");

_LIT8(kKeyword_MetaLeft, "meta-left");
_LIT8(kKeyword_MetaRight, "meta-right");
_LIT8(kKeyword_space, "space");
_LIT8(kKeyword_tab, "tab");
_LIT8(kKeyword_newline, "newline");

namespace sjson_template {
const TInt kGranularity = 8;

//////////////////////////////////
// CLiteralStatement
CLiteralStatement::CLiteralStatement(const TDesC8& aPlainText) : iPlainText(aPlainText)
	{
	// No implementation required
	}

CLiteralStatement* CLiteralStatement::NewL(const TDesC8& aPlainText)
	{
	CLiteralStatement* self = new (ELeave) CLiteralStatement(aPlainText);
	return self;
	}

void CLiteralStatement::ExecuteL(CScopedContext& /*context*/, MTemplateRenderCallback& callback)
	{
	callback.HandleTemplateRenderedStringL(iPlainText);
	}
//////////////////////////////////////
// CSectionStatement implementation
CSectionStatement::CSectionStatement(CSection& aSection) : iBlock(aSection)
	{
	// No implementation required
	}

CSectionStatement* CSectionStatement::NewL(CSection& aSection)
	{
	CSectionStatement* self = new (ELeave) CSectionStatement(aSection);
	return self;
	}

void CSectionStatement::ExecuteL(CScopedContext& context, MTemplateRenderCallback& callback)
	{
	// push a context first
	const TDesC8& name = iBlock.SectionName();
	CContext* cursorPosition = context.PushSectionL(name);

	// if context is not empty
	if (cursorPosition)
//	if (cursorPosition && !cursorPosition->Is<CContext::Null>())
		{
		CTemplate::_Execute(iBlock.Statements(), &context, callback);
//		TemplateExecutor.execute(this.block.getStatements(), context,
//				callback);
		context.Pop();
		}
	else
		{
		CTemplate::_Execute(iBlock.Statements(_L8("or")), &context, callback);
//		TemplateExecutor.execute(this.block.getStatements("or"), context,
//				callback);
		}
	}
//////////////////////////////////////
// CRepeatedSectionStatement implementation
CRepeatedSectionStatement::CRepeatedSectionStatement(CSection& aSection) : iBlock(aSection)
	{
	// No implementation required
	}

CRepeatedSectionStatement* CRepeatedSectionStatement::NewL(CSection& aSection)
	{
	CRepeatedSectionStatement* self = new (ELeave) CRepeatedSectionStatement(aSection);
	return self;
	}

void CRepeatedSectionStatement::ExecuteL(CScopedContext& aContext, MTemplateRenderCallback& aCallBack)
	{
	TBool pushed(EFalse);
	CContext* value = NULL;
	if (iBlock.SectionName().CompareF(_L8("@")) == 0)
		{
		// If the name is @, we stay in the enclosing context, but assume it's a
		// list, and repeat this block many times.
		value = aContext.CursorValue();
		// TODO: check that items is an array; apparently this is hard in JavaScript
		//if type(items) is not list:
		//  raise EvaluationError('Expected a list; got %s' % type(items))
		pushed = EFalse;
		}
	else
		{
		value = aContext.PushSectionL(iBlock.SectionName());
		pushed = ETrue;
		}

	__ASSERT_ALWAYS(value, Panic(ETemplateNullContext));
	__ASSERT_ALWAYS(value->Is<CJSONArray>(), Panic(ETemplateCurrentCursorNotArray));
	CJSONArray& items = value->Get<CJSONArray>();

	//log('ITEMS: '+showArray(items));
	if (items.Size() > 0)
		{
		// Execute the statements in the block for every item in the list.
		// Execute the alternate block on every iteration except the last.  Each
		// item could be an atom (string, integer, etc.) or a dictionary.
		TInt last_index = items.Size() - 1;
		RClauseArray* statements = iBlock.Statements(KNullDesC8); // default statements
		RClauseArray* alt_statements = iBlock.Statements(_L8("alternate"));

		for (TInt i=0; aContext.NextL(); i++)
			{
			LOGARG("_DoRepeatedSection i: %d", i);
			CTemplate::_Execute(statements, &aContext, aCallBack);
			if (alt_statements && i != last_index)
				{
				LOGTXT("ALTERNATE");
				CTemplate::_Execute(alt_statements, &aContext, aCallBack);
				}
			}
		}
	else
		{
//		LOGARG(_L8("OR: %S"), aBlock->Statements(_L8("or")));
		CTemplate::_Execute(iBlock.Statements(_L8("or")), &aContext, aCallBack);
		}

	if (pushed)
		{
		aContext.Pop();
		}
	}

//////////////////////////////////
// CSubstitutionStatement
CSubstitutionStatement::CSubstitutionStatement(const TDesC8& aName) : iName(aName)
	{
	// No implementation required
	}

CSubstitutionStatement* CSubstitutionStatement::NewL(const TDesC8& aName)
	{
	CSubstitutionStatement* self = new (ELeave) CSubstitutionStatement(aName);
	return self;
	}

void CSubstitutionStatement::ExecuteL(CScopedContext& aContext, MTemplateRenderCallback& callback)
	{
	CContext* cur = aContext.CursorValue();
	if (cur->Is<CJSONObject>())
		{
		CJSONObject& obj = cur->Get<CJSONObject>();
		if (obj.Has<TDesC8>(iName))
			callback.HandleTemplateRenderedStringL(obj.Get<TDesC8>(iName));
		else if (obj.Has<TInt64>(iName))
			{
			TBuf8<20> buf;
			buf.AppendNum(obj.Get<TInt64>(iName));
			callback.HandleTemplateRenderedStringL(buf);
			}
		else
			{
			callback.HandleTemplateRenderedStringL(iName);
			}
		}
	else
		{
		CDictValue* val = aContext.LookupL(iName);
		if (!val) return;
		if (val->Is<TDesC8>())
			{
			callback.HandleTemplateRenderedStringL(val->Get<TDesC8>());
			}
		else if (val->Is<TInt64>())
			{
			TBuf8<20> buf;
			buf.AppendNum(val->Get<TInt64>());
			callback.HandleTemplateRenderedStringL(buf);
			}
		else
			callback.HandleTemplateRenderedStringL(iName);
		}
	}
//////////////////////////////////
// CIndexStatement
CIndexStatement::CIndexStatement()
	{
	// No implementation required
	}

CIndexStatement* CIndexStatement::NewL()
	{
	CIndexStatement* self = new (ELeave) CIndexStatement();
	return self;
	}

void CIndexStatement::ExecuteL(CScopedContext& aContext, MTemplateRenderCallback& callback)
	{
	const CScopedContext::TItem& topStack = aContext.TopItem();
	TBuf8<20> num;
	num.AppendNum(topStack.iIndex);
	callback.HandleTemplateRenderedStringL(num);
	}

/////////////////////////////////
// CScopedContext implemenation

CScopedContext::CScopedContext()
	{
	// No implementation required
	}

CScopedContext::~CScopedContext()
	{
	iStack.Reset();
	iStack.Close();
	}

CScopedContext* CScopedContext::NewLC(CContext* aContext)
	{
	CScopedContext* self = new (ELeave) CScopedContext();
	CleanupStack::PushL(self);
	self->ConstructL(aContext);
	return self;
	}

CScopedContext* CScopedContext::NewL(CContext* aContext)
	{
	CScopedContext* self = CScopedContext::NewLC(aContext);
	CleanupStack::Pop(); // self;
	return self;
	}

void CScopedContext::ConstructL(CContext* aContext)
	{
	iStack.AppendL(TItem(aContext, -1));
	}

CContext* CScopedContext::PushSectionL(const TDesC8& aName)
	{
	if (aName.Length() == 0)
		return NULL;
	CContext * context = NULL;

	CContext* topContext = CursorValue();
	__ASSERT_ALWAYS(topContext->Is<CJSONObject>(), Panic(ETemplateCurrentCursorNotMap));
	CJSONObject& map = topContext->Get<CJSONObject>();
	context = map.GetL(aName);
	iStack.AppendL(TItem(context, -1));
	return context;
	}
void CScopedContext::Pop()
	{
	iStack.Remove(iStack.Count() - 1);
	}
const CScopedContext::TItem& CScopedContext::TopItem() const
	{
	return iStack[iStack.Count() - 1];
	}
TBool CScopedContext::NextL()
	{
	TItem* topStack = &iStack[iStack.Count() - 1];
//	TItem* topStack = &TopItem();

    // Now we're iterating -- push a new mutable object onto the stack
    if (topStack->iIndex == -1)
    	{
		iStack.AppendL(TItem(NULL, 0));
		topStack = &iStack[iStack.Count() - 1];
		}

    // The thing we're iterating over
    CContext* value = iStack[iStack.Count() - 2].iContext;
    CJSONArray& context_array = value->Get<CJSONArray>();
    // We're already done
    if (topStack->iIndex == context_array.Size() )
    	{
    	Pop();
    	LOGTXT("next: null");
    	return EFalse;  // sentinel to say that we're done
    	}

    LOGARG("next: %d", topStack->iIndex);

    topStack->iContext = &context_array[topStack->iIndex++];

    LOGTXT("next: true");
    return ETrue; // OK, we mutated the stack
	}

CContext* CScopedContext::CursorValue()
	{
	return iStack[iStack.Count() - 1].iContext;
	}

CDictValue* CScopedContext::LookupL(const TDesC8& aName)
	{
#if 1
    TInt i = iStack.Count() - 1;
    while(i>=0)
    	{
        CContext* context = iStack[i].iContext;
        LOGMEM(context);
        __ASSERT_DEBUG(context!=NULL, Panic(ETemplateNullContext));
        if (context->Is<CJSONObject>())
        	{
        	CJSONObject& obj = context->Get<CJSONObject>();
            CDictValue* val = obj.GetL(aName);
            if (val)  return val;
        	}
        --i;

//        if (i <= -1)
//        	{
//        	CDictValue* val = CDictValue::NewL(kUndefinedValue().Alloc());
//        	return val;
//        	}
        }
#endif
    return NULL;
	}

/////////////////////////////////
// CSection implemenation

CSection::CSection()
	{
	// No implementation required
	}

CSection::~CSection()
	{
	CleanMap();
	delete iSectionName;
	}

CSection* CSection::NewLC(const TDesC8& section_name, CSection* aParent)
	{
	CSection* self = new (ELeave) CSection();
	CleanupStack::PushL(self);
	self->ConstructL(section_name, aParent);
	return self;
	}

CSection* CSection::NewL(const TDesC8& section_name, CSection* aParent)
	{
	CSection* self = CSection::NewLC(section_name, aParent);
	CleanupStack::Pop(); // self;
	return self;
	}

void CSection::ConstructL(const TDesC8& section_name, CSection* aParent)
	{
	iParent = aParent;
	iCurrentClause = new (ELeave) RClauseArray(kGranularity);
	iStatements.InsertL(kDefaultClauseName().Alloc(), iCurrentClause);
	iSectionName = section_name.AllocL();
	}
void CSection::CleanMap()
	{
#if 0
	RClauseMap::TIter iter(iStatements);
#else
	RClauseMapIter iter(iStatements);
#endif
	const TDesC8* key = iter.NextKey();
	while (key)
		{
		RClauseArray* clause = const_cast<RClauseArray*>(iter.CurrentValue());
		clause->ResetAndDestroy();
		delete clause;
		delete key;
		key = iter.NextKey();
		}
	iStatements.Close();
	}
TPtrC8 CSection::SectionName()
	{
	return *iSectionName;
	}

RClauseArray* CSection::Statements(const TDesC8& aClauseName)
	{
	TPtrC8 clause(aClauseName);
	if (clause.Length() == 0) clause.Set(kDefaultClauseName);
	return iStatements.Find(clause);
	}

void CSection::NewClauseL(const TDesC8& aClauseName)
	{
	RClauseArray* new_clause = new (ELeave) RClauseArray(kGranularity);
	CleanupDeletePushL(new_clause);
	iStatements.InsertL(aClauseName.Alloc(), new_clause);
	CleanupStack::Pop(new_clause);
    iCurrentClause = new_clause;
	}

void CSection::AppendL(MStatement* aStatement)
	{
	iCurrentClause->AppendL(aStatement);
	}

void CSection::AppendL(const TDesC8& aPlainText)
	{
	MStatement* statement = CLiteralStatement::NewL(aPlainText);
	iCurrentClause->AppendL(statement);
	}


////////////////////////////////
// CTemplate implementation
void CTemplate::SplitL(CPtrC8Array& aParts, const TDesC8& aToken, TUint8 aSepChar)
	{
	TInt len(0);
	while (1)
		{
		TPtrC8 left = aToken.Mid(len);
		TInt pos = left.Locate(aSepChar);
		if (pos == KErrNotFound)
			break;
		aParts.AppendL(left.Mid(0, pos));
		++pos;  // skip seperator char
		len += pos;
		}
	aParts.AppendL(aToken.Mid(len));
	}

CTemplate::CTemplate() : iFormatChar('|'), iMeta(kDefaultMeta)
	{
	// No implementation required
	}

CTemplate::~CTemplate()
	{
	iStack.ResetAndDestroy();
	iStack.Close();
	delete iResult;
	}

CTemplate* CTemplate::NewLC(const TDesC8& aTemplateStr)
	{
	CTemplate* self = new (ELeave) CTemplate();
	CleanupStack::PushL(self);
	self->ConstructL(aTemplateStr);
	return self;
	}

CTemplate* CTemplate::NewL(const TDesC8& aTemplateStr)
	{
	CTemplate* self = CTemplate::NewLC(aTemplateStr);
	CleanupStack::Pop(); // self;
	return self;
	}

void CTemplate::ConstructL(const TDesC8& aTemplateStr)
	{
	iMeta.Copy(kDefaultMeta);
//	iRootBlock = _CompileL(aTemplateStr);
	iCurrentBlock = CSection::NewL(KNullDesC8);
	iRootBlock = iCurrentBlock;
	iStack.AppendL(iCurrentBlock);
	_CompileL(aTemplateStr);
	}

void CTemplate::_Execute(RClauseArray* aStatements, CScopedContext* aContext, MTemplateRenderCallback& aCallBack)
	{
  	for (TInt i=0; i<aStatements->Count(); ++i)
  		{
  		MStatement* statement = (*aStatements)[i];
//  		LOGMEM(statement);
  		statement->ExecuteL(*aContext, aCallBack);
  		}
  	}

TInt CTemplate::_MatchToken(const TDesC8& aData, TPtrC8& aTokenMatch)
	{
	TInt pos = aData.Find(iMetaLeft);
	if (pos >= 0)
		{
		TPtrC8 data = aData.Mid(pos);
		TInt pos2 = data.Find(iMetaRight);
		if (pos2 >= 0)
			aTokenMatch.Set(data.Mid(0, pos2+iMetaRight.Length()));
		}
	return pos;
	}

TPtrC8 CTemplate::_MatchSection(const TDesC8& aToken, TBool& aRepeated)
	{
	aRepeated = EFalse;
	if (aToken.FindF(_L8("repeated")) >= 0)
		{
		aRepeated = ETrue;
		}
	_LIT8(kKeyword_section, "section");
	TInt pos = aToken.FindF(kKeyword_section);
	if (pos == KErrNotFound) return TPtrC8();
	return aToken.Mid( pos + kKeyword_section().Length() + 1); // +1: skip space after section
	}

TInt CTemplate::_MatchSimpleKeywords(const TDesC8& aToken)
	{
	const TDesC8& token = aToken;
	if (token.CompareF(_L8("meta-left")) == 0)
		{
		iCurrentBlock->AppendL(iMetaLeft);
		}
	else if (token.CompareF(_L8("meta-right")) == 0)
		{
		iCurrentBlock->AppendL(iMetaRight);
		}
	else if (token.CompareF(_L8("space")) == 0)
		{
		iCurrentBlock->AppendL(_L8(" "));
		}
	else if (token.CompareF(_L8("tab")) == 0)
		{
		iCurrentBlock->AppendL(_L8("\t"));
		}
	else if (token.CompareF(_L8("newline")) == 0)
		{
		iCurrentBlock->AppendL(_L8("\n"));
		}
	else
		{
		return KErrNotFound;
		}
	return KErrNone;
	}

CSection* CTemplate::_CompileL(const TDesC8& aTemplateStr)
	{
	__ASSERT_ALWAYS(iMeta.Length()%2 != 1, Panic(ETemplateBadMeta));
	iMetaLeft.Set(iMeta.Mid(0, iMeta.Length() / 2));
	iMetaRight.Set(iMeta.Mid(iMeta.Length()/2, iMeta.Length() / 2));

	TInt stackLevel(0);

	TInt lastIndex(0);
#if 1
	TInt strip_num = iMetaLeft.Length();  // assume they're the same length
	while (ETrue)
		{
		TPtrC8 data = aTemplateStr.Mid(lastIndex);
		TPtrC8 token;
		TInt index = _MatchToken(data, token);
		if (token.Length() == 0)
			break;
		if (index > 0)
			{
			TPtrC8 tok = data.Mid(0, index);
			iCurrentBlock->AppendL(tok);
			if (tok.Length()< 200)
				{
				LOGARG("tok: %S", &tok);
				}
			else
				{
				LOGARG("tok: %d bytes", tok.Length());
				}
			}
		lastIndex += index;
		lastIndex += token.Length();
	    LOGARG("token: %S", &token);
	    TBool had_newline(EFalse);
	    if (token[token.Length() - 1] == '\n')
	    	{
	    	token.Set(token.Mid(0, token.Length() - 1));
	    	had_newline = ETrue;
	    	}

//	    token = token.Mid(iMetaLeft.Length(), token.Length() - iMetaLeft.Length() - iMetaRight.Length());
	    token.Set(token.Mid(strip_num, token.Length()- strip_num*2));

	    if (token[0] == '#')
	    	{
	    	continue;  // comment
	    	}

	    if (token[0] == '.')
	    	{  // Keyword
	    	token.Set(token.Mid(1));
	    	// metch simple keywords
	    	TInt ret = _MatchSimpleKeywords(token);
	    	if (ret == KErrNone)
	    		continue;
	    	if (token.CompareF(_L8("index"))==0)
	    		{
	    		MStatement* statement = CIndexStatement::NewL();
	    		iCurrentBlock->AppendL(statement);
	    		continue;
	    		}

		      // match section
	    	TBool repeated(EFalse);
	    	TPtrC8 section_name = _MatchSection(token, repeated);
	    	if (section_name.Length() > 0)
	    		{
	    		_NewSectionL(repeated, section_name, iCurrentBlock);
	    		++stackLevel;
		        continue;
	    		}

//	      _LIT8(kKeyword_alternated_with, "alternates with");
	    	if (token.CompareF(_L8("alternates with")) == 0)
	    		{
	    		iCurrentBlock->NewClauseL(_L8("alternate"));
	    		continue;
	    		}

	    	if (token.CompareF(_L8("or")) == 0)
	    		{
	    		iCurrentBlock->NewClauseL(_L8("or"));
	    		continue;
	    		}
	    	if (token.CompareF(_L8("end")) == 0)
	    		{
	    		// End the block
	    		--stackLevel;
	    		iCurrentBlock = iCurrentBlock->Parent();
	    		__ASSERT_ALWAYS(iCurrentBlock != 0, Panic(ETemplateSyntaxError));
#if 0
	    		TInt topIndex = iStack.Count() - 1;
	    		CSection* topSection = iStack[topIndex];
	    		delete topSection;
	    		iStack.Remove(topIndex);

	    		__ASSERT_ALWAYS(iStack.Count() > 0, Panic(ETemplateSyntaxError));
	    		if (iStack.Count() > 0)
	    			{
	    			iCurrentBlock = iStack[iStack.Count() - 1];
	    			}
#endif
	    		continue;
	    		}
	    	}

		// A variable substitution
#if 0
	    CPtrC8Array *parts = new (ELeave)CPtrC8Array(5);
	    CleanupStack::PushL(parts);
	    SplitL(*parts, token, iFormatChar);
	    CDesC8Array* formatters;
	    TPtrC8 name;
	    if (parts->Count() == 1)
	    	{
	    	__ASSERT_ALWAYS(iDefaultFormatter.Length() > 0, Panic(ETemplateMissingFormatter));
	    	// If no formatter is specified, the default is the 'str' formatter,
	    	// which the user can define however they desire.
	    	formatters = [GetFormatter(iDefaultFormatter)];
	    	name = token;
	    	}
	    else
	    	{
	    	formatters = [];
	    	for (var j=1; j<parts.length; j++)
	    		{
	    		formatters.push(GetFormatter(parts[j]));
	    		}
	    	name = parts[0];
	    	}
	    current_block.Append([_DoSubstitute, { name: name, formatters: formatters}]);
#else
	    MStatement* statement = CSubstitutionStatement::NewL(token);
	    iCurrentBlock->AppendL(statement);
#endif
	    if (had_newline)
	    	{
	    	iCurrentBlock->AppendL(CLiteralStatement::NewL(_L8("\n")));
	    	}
	    }
#endif
	// Add the trailing literal
//	iCurrentBlock->AppendL(CLiteralStatement::NewL(aTemplateStr.Mid(lastIndex)));
	__ASSERT_DEBUG(stackLevel == 0, Panic(ETemplateSyntaxError));

	return iCurrentBlock;
	}

void CTemplate::_NewSectionL(TBool aRepeated, const TDesC8& aSectionName, CSection* aParent)
	{
	LOGENTER;
    LOGARG("repeated %d, section_name: %S", aRepeated, &aSectionName);

    CSection* new_block = CSection::NewLC(aSectionName, aParent);
    if (aRepeated)
    	{
        MStatement* statement = CRepeatedSectionStatement::NewL(*new_block);
        iCurrentBlock->AppendL(statement);
    	}
    else
    	{
    	MStatement* statement = CSectionStatement::NewL(*new_block);
        iCurrentBlock->AppendL(statement);
        }
    iStack.Append(new_block);
    CleanupStack::Pop(new_block);
    iCurrentBlock = new_block;
    LOGEXIT;
	}
void CTemplate::RenderL(CContext* aRootContext, MTemplateRenderCallback* aCallback)
	{
//	LOGENTER;
	CScopedContext* context = CScopedContext::NewLC(aRootContext);

	CTemplate::_Execute(iRootBlock->Statements(), context, *(aCallback ? aCallback : this));
	CleanupStack::PopAndDestroy(context);
//	LOGEXIT;
	}

void CTemplate::ExpandL(const TDesC8& aJsonData)
	{
//	LOGENTER;
	CContext* value = CContext::NewL();
	CleanupStack::PushL(value);
	TInt ret = value->Parse(aJsonData);
	User::LeaveIfError(ret);
	this->RenderL(value, iCallback ? iCallback : this);
	CleanupStack::PopAndDestroy(value);
//	LOGEXIT;
	}

void CTemplate::HandleTemplateRenderedStringL(const TDesC8& aRenderedString)
	{
	LOGDES8(aRenderedString);
	const TInt blockSize = 1024;
	if (iResult)
		{
		TPtr8 ptr = iResult->Des();
		if (ptr.MaxLength() -  ptr.Length() < aRenderedString.Length())
			{
			iResult = iResult->ReAllocL(iResult->Length() + Max(blockSize, aRenderedString.Length()));
			}
		iResult->Des() += aRenderedString;
		}
	else
		{
		iResult = HBufC8::NewL(Max(blockSize, aRenderedString.Length()));
		iResult->Des().Copy(aRenderedString);
		}
	}

TPtrC8 CTemplate::Result() const
	{
	if (iResult)
		return *iResult;
	return TPtrC8();
	}

} // namespace sjson_template
